package com.bsj.media.media.resample;

import com.bsj.media.media.resample.basic.resamplerBasic;
import com.bsj.media.media.resample.basic.resamplerBasicFactory;
import com.bsj.media.media.resample.quality.FuncDef;
import com.bsj.media.media.resample.quality.QualityMapping;

public class resampler {

	private resamplerState m_resamplerState;
	private resamplerBasic m_resamplerBasic;

	private static int INT_MAX = 2147483647;
	private static int FIXED_STACK_ALLOC = 1024;

	private static FuncDef st_KAISER12 = FuncDef.setKAISER12();
	private static FuncDef st_KAISER10 = FuncDef.setKAISER10();
	private static FuncDef st_KAISER8 = FuncDef.setKAISER8();
	private static FuncDef st_KAISER6 = FuncDef.setKAISER6();
	private static QualityMapping[] quality_map = QualityMapping.setQualityMap(st_KAISER12, st_KAISER10, st_KAISER8,
			st_KAISER6);

	final int word2int(float x) {
		if (x < -32767.5f) {
			return -32768;
		} else {
			if (x > 32766.5f) {
				return 32767;
			} else {
				return (int) (0.5 + x);
			}
		}
	}

	final int IMAX(int a, int b) {
		return ((a) > (b) ? (a) : (b));
	}

	final int IMIN(int a, int b) {
		return ((a) < (b) ? (a) : (b));
	}

	public int speex_resampler_skip_zeros() {
		int i;
		for (i = 0; i < m_resamplerState.nb_channels; i++)
			m_resamplerState.last_sample[i] = m_resamplerState.filt_len / 2;
		return resamplerErr.RESAMPLER_ERR_SUCCESS;
	}

	public int speex_resampler_init(int nb_channels, int in_rate, int out_rate, int quality, int err) {
		m_resamplerState = speex_resampler_init_frac(nb_channels, in_rate, out_rate, in_rate, out_rate, quality, err);
		if (null != m_resamplerState) {
			return 1;
		}
		return 0;
	}

	public int speex_resampler_process_int(int channel_index, final short[] in, int in_len, short[] out, int out_len) {
		int j;
		resamplerState st = m_resamplerState;
		final int istride_save = st.in_stride;
		final int ostride_save = st.out_stride;
		int ilen = in_len;
		int olen = out_len;
		// float *x = st.mem + channel_index * st.mem_alloc_size;
		final int xlen = st.mem_alloc_size - (st.filt_len - 1);

		final int ylen = FIXED_STACK_ALLOC;
		float[] ystack = new float[FIXED_STACK_ALLOC];

		st.out_stride = 1;
		int offset_in = 0;
		int offset_out = 0;
		while (0 != ilen && 0 != olen) {
			int ichunk = (ilen > xlen) ? xlen : ilen;
			int ochunk = (olen > ylen) ? ylen : olen;
			int omagic = 0;

			if (0 != st.magic_samples[channel_index]) {
				omagic = speex_resampler_magic(st, channel_index, ystack, ochunk);
				ochunk -= omagic;
				olen -= omagic;
			}
			if (0 == st.magic_samples[channel_index]) {
				if (null != in) {
					for (j = 0; j < ichunk; ++j) {						 
						st.mem[channel_index * st.mem_alloc_size + j + st.filt_len - 1] = in[offset_in
								+ j * istride_save];
						}
				} else {
					for (j = 0; j < ichunk; ++j)
						st.mem[channel_index * st.mem_alloc_size + j + st.filt_len - 1] = 0;
				}

				ochunk = speex_resampler_process_native(st, channel_index, ichunk, ystack, ochunk);
			} else {
				ichunk = 0;
				ochunk = 0;
			}

			for (j = 0; j < ochunk + omagic; ++j) {
				out[offset_out + j * ostride_save] = (short) word2int(ystack[j]);
			}

			ilen -= ichunk;
			olen -= ochunk;
			offset_out += (ochunk + omagic) * ostride_save;
			offset_in += ichunk * istride_save;
		}
		st.out_stride = ostride_save;
		in_len -= ilen;
		out_len -= olen;

		if (m_resamplerBasic.getType() == 0) {
			return 0;
		}

		return out_len;
	}

	public int speex_resampler_process_int(int channel_index, final byte[] in, int in_len, byte[] out, int out_len) {
		int j;
		resamplerState st = m_resamplerState;
		final int istride_save = st.in_stride;
		final int ostride_save = st.out_stride;
		int ilen = in_len;
		int olen = out_len;
		// float *x = st.mem + channel_index * st.mem_alloc_size;
		final int xlen = st.mem_alloc_size - (st.filt_len - 1);

		final int ylen = FIXED_STACK_ALLOC;
		float[] ystack = new float[FIXED_STACK_ALLOC];

		st.out_stride = 1;
		int offset_in = 0;
		int offset_out = 0;
		while (0 != ilen && 0 != olen) {
			int ichunk = (ilen > xlen) ? xlen : ilen;
			int ochunk = (olen > ylen) ? ylen : olen;
			int omagic = 0;

			if (0 != st.magic_samples[channel_index]) {
				omagic = speex_resampler_magic(st, channel_index, ystack, ochunk);
				ochunk -= omagic;
				olen -= omagic;
			}
			if (0 == st.magic_samples[channel_index]) {
				if (null != in) {
					int pos = 0;
					for (j = 0; j < ichunk; ++j) {
						pos = offset_in+ j * istride_save;
						st.mem[channel_index * st.mem_alloc_size + j + st.filt_len - 1] = (short) (in[pos * 2+1] << 8 | in[2 *pos] & 0xff);
						}
				} else {
					for (j = 0; j < ichunk; ++j)
						st.mem[channel_index * st.mem_alloc_size + j + st.filt_len - 1] = 0;
				}

				ochunk = speex_resampler_process_native(st, channel_index, ichunk, ystack, ochunk);
			} else {
				ichunk = 0;
				ochunk = 0;
			}

			int pos = 0;
			int tmp = 0;
			for (j = 0; j < ochunk + omagic; ++j) {
				tmp = (int) word2int(ystack[j]);
				pos = offset_out + j * ostride_save;
				out[pos * 2+1] = (byte) (tmp>> 8);
				out[pos * 2] = (byte) (tmp >> 0);
			}

			ilen -= ichunk;
			olen -= ochunk;
			offset_out += (ochunk + omagic) * ostride_save;
			offset_in += ichunk * istride_save;
		}
		st.out_stride = ostride_save;
		in_len -= ilen;
		out_len -= olen;

		if (m_resamplerBasic.getType() == 0) {
			return 0;
		}

		return out_len;
	}

	
	private resamplerState speex_resampler_init_frac(int nb_channels, int ratio_num, int ratio_den, int in_rate,
			int out_rate, int quality, int err) {
		int i;
		int filter_err;

		if (quality > 10 || quality < 0) {
			err = resamplerErr.RESAMPLER_ERR_INVALID_ARG;
			return null;
		}

		resamplerState st = new resamplerState();
		st.initialised = 0;
		st.started = 0;
		st.in_rate = 0;
		st.out_rate = 0;
		st.num_rate = 0;
		st.den_rate = 0;
		st.quality = -1;
		st.sinc_table_length = 0;
		st.mem_alloc_size = 0;
		st.filt_len = 0;

		st.cutoff = 1.f;
		st.nb_channels = nb_channels;
		st.in_stride = 1;
		st.out_stride = 1;

		st.buffer_size = 160;

		/* Per channel data */
		st.last_sample = new int[nb_channels];
		st.magic_samples = new int[nb_channels];
		st.samp_frac_num = new int[nb_channels];
		for (i = 0; i < nb_channels; i++) {
			st.last_sample[i] = 0;
			st.magic_samples[i] = 0;
			st.samp_frac_num[i] = 0;
		}

		speex_resampler_set_quality(st, quality);
		speex_resampler_set_rate_frac(st, ratio_num, ratio_den, in_rate, out_rate);

		filter_err = update_filter(st);
		if (filter_err == resamplerErr.RESAMPLER_ERR_SUCCESS) {
			st.initialised = 1;
		} else {
			st = null;
		}

		err = filter_err;
		return st;
	}

	int update_filter(resamplerState st) {
		int old_length = st.filt_len;
		int old_alloc_size = st.mem_alloc_size;
		boolean use_direct;
		int min_sinc_table_length;
		int min_alloc_size;

		st.int_advance = st.num_rate / st.den_rate;
		st.frac_advance = st.num_rate % st.den_rate;
		st.oversample = quality_map[st.quality].oversample;
		st.filt_len = quality_map[st.quality].base_length;

		if (st.num_rate > st.den_rate) {
			/* down-sampling */
			st.cutoff = quality_map[st.quality].downsample_bandwidth * st.den_rate / st.num_rate;
			/*
			 * FIXME: divide the numerator and denominator by a certain amount if they're
			 * too large
			 */
			st.filt_len = st.filt_len * st.num_rate / st.den_rate;
			/* Round up to make sure we have a multiple of 8 for SSE */
			st.filt_len = ((st.filt_len - 1) & (~0x7)) + 8;
			if (2 * st.den_rate < st.num_rate)
				st.oversample >>= 1;
			if (4 * st.den_rate < st.num_rate)
				st.oversample >>= 1;
			if (8 * st.den_rate < st.num_rate)
				st.oversample >>= 1;
			if (16 * st.den_rate < st.num_rate)
				st.oversample >>= 1;
			if (st.oversample < 1)
				st.oversample = 1;
		} else {
			/* up-sampling */
			st.cutoff = quality_map[st.quality].upsample_bandwidth;
		}

		use_direct = st.filt_len * st.den_rate <= st.filt_len * st.oversample + 8
				&& INT_MAX / 4 / st.den_rate >= st.filt_len;

		if (use_direct) {
			min_sinc_table_length = st.filt_len * st.den_rate;
		} else {
			if ((INT_MAX / 4 - 8) / st.oversample < st.filt_len) {
				return failDeal(st, old_length, use_direct, st.quality);
			}
			min_sinc_table_length = (int) (st.filt_len * st.oversample + 8);
		}
		if (st.sinc_table_length < min_sinc_table_length) {
			float[] sinc_table = new float[min_sinc_table_length];

			if (null != st.sinc_table) {
				System.arraycopy(st.sinc_table, 0, sinc_table, 0, st.sinc_table_length);
			}
			st.sinc_table = sinc_table;
			st.sinc_table_length = min_sinc_table_length;
		}
		if (use_direct) {
			int i;
			for (i = 0; i < st.den_rate; i++) {
				int j;
				for (j = 0; j < st.filt_len; j++) {
					st.sinc_table[i * st.filt_len + j] = FuncDef.sinc(st.cutoff,
							((j - (int) st.filt_len / 2 + 1) - ((float) i) / st.den_rate), st.filt_len,
							quality_map[st.quality].window_func);
				}
			}
		} else {
			int i;
			for (i = -4; i < (int) (st.oversample * st.filt_len + 4); i++)
				st.sinc_table[i + 4] = FuncDef.sinc(st.cutoff, (i / (float) st.oversample - st.filt_len / 2),
						st.filt_len, quality_map[st.quality].window_func);
		}

		m_resamplerBasic = resamplerBasicFactory.createResamplerBasic(use_direct, st.quality, false);

		min_alloc_size = st.filt_len - 1 + st.buffer_size;
		if (min_alloc_size > st.mem_alloc_size) {
			if (INT_MAX / 4 / st.nb_channels < min_alloc_size) {
				return failDeal(st, old_length, use_direct, st.quality);
			}

			float[] mem = new float[st.nb_channels * min_alloc_size];

			if (null != st.mem)
				System.arraycopy(st.mem, 0, mem, 0, st.mem.length);
			st.mem = mem;
			st.mem_alloc_size = min_alloc_size;
		}

		if (0 == st.started) {
			int i;
			for (i = 0; i < st.nb_channels * st.mem_alloc_size; i++) {
				st.mem[i] = 0;
			}
			/* speex_warning("reinit filter"); */
		} else if (st.filt_len > old_length) {
			int i;
			/* Increase the filter length */
			/* speex_warning("increase filter size"); */
			for (i = st.nb_channels - 1; i >= 0; i--) {
				int j;
				int olen = old_length;
				/* if (st.magic_samples[i]) */
				{
					/* Try and remove the magic samples as if nothing had happened */

					/*
					 * FIXME: This is wrong but for now we need it to avoid going over the array
					 * bounds
					 */
					olen = old_length + 2 * st.magic_samples[i];
					for (j = old_length - 2 + st.magic_samples[i]; j >= 0; j--)
						st.mem[i * st.mem_alloc_size + j + st.magic_samples[i]] = st.mem[i * old_alloc_size + j];
					for (j = 0; j < st.magic_samples[i]; j++)
						st.mem[i * st.mem_alloc_size + j] = 0;
					st.magic_samples[i] = 0;
				}
				if (st.filt_len > olen) {
					/* If the new filter length is still bigger than the "augmented" length */
					/* Copy data going backward */
					for (j = 0; j < olen - 1; j++)
						st.mem[i * st.mem_alloc_size + (st.filt_len - 2 - j)] = st.mem[i * st.mem_alloc_size
								+ (olen - 2 - j)];
					/* Then put zeros for lack of anything better */
					for (; j < st.filt_len - 1; j++)
						st.mem[i * st.mem_alloc_size + (st.filt_len - 2 - j)] = 0;
					/* Adjust last_sample */
					st.last_sample[i] += (st.filt_len - olen) / 2;
				} else {
					/* Put back some of the magic! */
					st.magic_samples[i] = (olen - st.filt_len) / 2;
					for (j = 0; j < st.filt_len - 1 + st.magic_samples[i]; j++)
						st.mem[i * st.mem_alloc_size + j] = st.mem[i * st.mem_alloc_size + j + st.magic_samples[i]];
				}
			}
		} else if (st.filt_len < old_length) {
			int i;
			/*
			 * Reduce filter length, this a bit tricky. We need to store some of the memory
			 * as "magic" samples so they can be used directly as input the next time(s)
			 */
			for (i = 0; i < st.nb_channels; i++) {
				int j;
				int old_magic = st.magic_samples[i];
				st.magic_samples[i] = (old_length - st.filt_len) / 2;
				/* We must copy some of the memory that's no longer used */
				/* Copy data going backward */
				for (j = 0; j < st.filt_len - 1 + st.magic_samples[i] + old_magic; j++)
					st.mem[i * st.mem_alloc_size + j] = st.mem[i * st.mem_alloc_size + j + st.magic_samples[i]];
				st.magic_samples[i] += old_magic;
			}
		}
		return resamplerErr.RESAMPLER_ERR_SUCCESS;
	}

	int failDeal(resamplerState st, int old_length, boolean use_direct, int quality) {
		m_resamplerBasic = resamplerBasicFactory.createResamplerBasic(use_direct, st.quality, true);
		st.filt_len = old_length;
		return resamplerErr.RESAMPLER_ERR_ALLOC_FAILED;
	}

	int speex_resampler_magic(resamplerState st, int channel_index, float[] out, int out_len) {
		int tmp_in_len = st.magic_samples[channel_index];
		// float *mem = st.mem + channel_index * st.mem_alloc_size;
		final int N = st.filt_len;

		out_len = speex_resampler_process_native(st, channel_index, tmp_in_len, out, out_len);

		st.magic_samples[channel_index] -= tmp_in_len;

		/*
		 * If we couldn't process all "magic" input samples, save the rest for next time
		 */
		if (0 != st.magic_samples[channel_index]) {
			int i;
			for (i = 0; i < st.magic_samples[channel_index]; i++)
				st.mem[channel_index * st.mem_alloc_size + N - 1 + i] = st.mem[channel_index * st.mem_alloc_size + N - 1
						+ i + tmp_in_len];
		}
		return out_len;
	}

	int speex_resampler_process_native(resamplerState st, int channel_index, int in_len, float[] out, int out_len) {
		int j = 0;
		final int N = st.filt_len;
		int out_sample = 0;
		int ilen;

		st.started = 1;

		/* Call the right resampler through the function ptr */

		out_sample = m_resamplerBasic.resampler_basic_func(st, channel_index, st.mem, 
				channel_index * st.mem_alloc_size, in_len, out, out_len);

		if (st.last_sample[channel_index] < in_len)
			in_len = st.last_sample[channel_index];
		out_len = out_sample;
		st.last_sample[channel_index] -= in_len;

		ilen = in_len;

		final int offset = channel_index * st.mem_alloc_size;
		for (j = 0; j < N - 1; ++j)
			st.mem[offset+j] = st.mem[offset+j + ilen];

		return out_len;
	}

	public int speex_resampler_set_rate(resamplerState st, int in_rate, int out_rate) {
		return speex_resampler_set_rate_frac(st, in_rate, out_rate, in_rate, out_rate);
	}

	private int speex_resampler_set_rate_frac(resamplerState st, int ratio_num, int ratio_den, int in_rate,
			int out_rate) {
		int fact = 0;
		int old_den = 0;
		int i = 0;
		if (st.in_rate == in_rate && st.out_rate == out_rate && st.num_rate == ratio_num && st.den_rate == ratio_den)
			return resamplerErr.RESAMPLER_ERR_SUCCESS;

		old_den = st.den_rate;
		st.in_rate = in_rate;
		st.out_rate = out_rate;
		st.num_rate = ratio_num;
		st.den_rate = ratio_den;
		/* FIXME: This is terribly inefficient, but who cares (at least for now)? */
		for (fact = 2; fact <= IMIN(st.num_rate, st.den_rate); fact++) {
			while ((st.num_rate % fact == 0) && (st.den_rate % fact == 0)) {
				st.num_rate /= fact;
				st.den_rate /= fact;
			}
		}

		if (old_den > 0) {
			for (i = 0; i < st.nb_channels; i++) {
				st.samp_frac_num[i] = st.samp_frac_num[i] * st.den_rate / old_den;
				/* Safety net */
				if (st.samp_frac_num[i] >= st.den_rate)
					st.samp_frac_num[i] = st.den_rate - 1;
			}
		}

		if (1 == st.initialised)
			return update_filter(st);
		return resamplerErr.RESAMPLER_ERR_SUCCESS;
	}

	private int speex_resampler_set_quality(resamplerState st, int quality) {
		if (quality > 10 || quality < 0)
			return resamplerErr.RESAMPLER_ERR_INVALID_ARG;
		if (st.quality == quality)
			return resamplerErr.RESAMPLER_ERR_SUCCESS;
		st.quality = quality;
		if (1 == st.initialised)
			return update_filter(st);
		return resamplerErr.RESAMPLER_ERR_SUCCESS;
	}
}
